#ifndef INTRINSIC_SOLVER_H
#define INTRINSIC_SOLVER_H 1

//intrinsic solver for solving sparse linear equation

#include "cvxImage.h"
#include <map>
#include "least_square.h"
#include "cvxQuadTree.h"
#include "cvxSimilarColorQuadTree.h"

using std::map;




//data for speed up solving local shading
struct SLocalShadingData
{
	Mat _shadingMap;
	Mat _reflectanceMap;
	Mat _idxMap;
	Mat _edgeMap;
	Mat _orgImg;
	int _avgCol[3];
	int _unknowNum;
	float _baseLine;
	float _step;

	LeastSquare _solver;	

};


class IntrinsicSolver
{
public:
	IntrinsicSolver();
	~IntrinsicSolver();

	//general interface
	void setImg(const Mat &img);
	bool isEmpty(void);
	int width(void);
	int height(void);
	void GetOrgImg(Mat &img);
	void GetEdgemap(Mat &edgemap, const cv::Rect *pRect);
	void GetMiniShading(Mat &miniSha, const cv::Rect *pRect);
	

	// compute local shading
	bool initSolveLocalShading(const Mat &strokeMap, int label, 
							   const Mat &edgeMap, const Mat &img);
	bool updateSolveLocalShading(float minShading, bool showResult);
	
	// date exchange in local shading
	void GetLocalShadingBaselineAndStep(float &baseline, float &step);
	void GetLocalShadingMap(Mat &outMat);
	void GetLocalReflectanceMap(Mat &outMat);


	//set E tree node
	bool setETree(const Mat &img, const Mat &edgeMap, const Mat &knownMap, 
				  Mat &shadingMap, Mat &reflectanceMap);
	// compute shading map with help of quadtree for initial unknown area
	bool ETreeSolver(const Mat &img, Mat &shadingMap, Mat &reflectanceMap);

	bool setMTree(bool reverse);
	//reverse known and unknown area to get the shading value of known area
	bool MTreeSolver(const Mat &img, Mat &shadingMap, Mat &reflectanceMap);

	bool UpSampleAVector(const Mat &img, Mat &shadingMap, Mat &reflectanceMap);

	// solver equation 8 to get (ar, ag, ab)
	bool GetColorPlaneNormalFromShading(const Mat &img, const Mat &shadingMap, Mat &AMap);

	//
	void computeEdgeMap(float threshold = 0.999);
	void computeMiniShading();

	

public:
	//segment one path two multiple path according to the sampled pixel color
	void SegmentPath(const vector<cv::Point> &path, float threshold, vector<vector<cv::Point>> &pathes);
	static cv::Rect GetBoundingBox(int w, int h, int penWidth, 
						const vector<cv::Point> &pathPts, int expandWidth);

private:
	Mat m_orgImg;
	Mat m_edgeMap;         //set which pixel as edge  8UC1
	Mat m_miniShading;

	SLocalShadingData m_lSolver;

	CvxSimColorQuadTree *m_pEtree;      //quadtree to speed up least square solver
	CvxSimColorQuadTree *m_pMtree;      //quadtree to reverse m_pEtree

private:
	//return minimal shading value in the stroke area
	static void GetIndexMap(const Mat &strokeMap, int label, const Mat &orgImage, 
						    Mat &indexMap, int &unKnownNum, float &minShading, int *avgCol);

	//compute fixed shading value in the stroke area
	static void GetFixedShading(const Mat &idxMap, const Mat &orgImage,
								float minShading, int *avgCol, Mat &shadingMap, Mat &reflectanceMap);

	//assume the local color distribution is in a plane
	//equation 10 in "User-assisted intrinsic images"
	static void GetEquationAtLocalWindow(const Mat &idxMap, const Mat &shadingMap, const Mat &edgeMap, 
					const Mat &orgImage, 
					vector<map<int, double>> &matrix_vec, 
					vector<double> &right_vec, bool needLeft);

	//set equation at edge 
	/************************************************************************/
	/*   0 1 2
	     3 4 5
		 6 7 8
		 (s4-s3)/(I4-I3) = (s5-s4)/(I5-I4)
	*/
	/************************************************************************/
	static void GetEquationAtEdge(const Mat &idxMap, const Mat &shadingMap, const Mat &edgeMap, 
									const Mat &orgImage, 
									vector<map<int, double>> &matrix_vec, 
									vector<double> &right_vec);

	//rectify shading position where is out rang of [min, 1.0]
	static void CorrectErrorMap(const Mat &idxMap, const Mat &errorMap, const Mat &rectifyMap, 
								vector<map<int, double>> &matrix_vec, vector<double> &right_vec);

};


#endif